package main

import (
	"fmt"
	_ "github.com/codyguo/godaemon"
	"github.com/sirupsen/logrus"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	TotalNumberConnections = 0
	mutex                  sync.Mutex
	timeout                = 20 * time.Minute
)

func init() {
	logrus.SetLevel(logrus.DebugLevel)
	logFile, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err == nil {
		logrus.SetOutput(logFile)
	} else {
		logrus.Info("打开日志文件失败，默认输出到stderr")
	}
	logrus.SetFormatter(&logrus.TextFormatter{
		FullTimestamp:   true,
		TimestampFormat: "2006-01-02 15:04:05", // 设置时间格式
	})
	fmt.Println("端口转发软件开始运行!")
	logrus.Debug("端口转发软件开始运行!")
	logrus.Debug("软件版本2.2")

}
func removeNewlines(input string) string {
	return strings.Map(func(r rune) rune {
		if r != '\n' && r != '\r' {
			return r
		}
		return -1
	}, input)
}

func Run() {
	FileName := "portForward.conf"
	fi, err := os.ReadFile(FileName)
	if err != nil {
		logrus.Error("读取配置文件错误:" + err.Error())
		return
	}
	split := strings.Split(string(fi), "\n")
	var fromportLis, toportList []string
	// 开始循环解析配置文件
	for _, val := range split {
		if !strings.Contains(val, "#") && len(strings.TrimSpace(val)) > 0 {
			confIp := strings.Split(val, " ")
			if len(confIp) == 2 {
				fromportLis = append(fromportLis, removeNewlines(confIp[0]))
				toportList = append(toportList, removeNewlines(confIp[1]))
			}
		}
	}
	logrus.Debug("配置文件解析完成开始进行流量转发!!!")
	// 每一个转发规则,开启一个协程去处理
	for i, val := range fromportLis {
		fmt.Println(val, "---->", toportList[i])
		logrus.Debug(val, "---->", toportList[i])
		go forward(val, toportList[i])
	}
	logrus.Debug("开启完成!")
	pid := strconv.Itoa(os.Getpid())
	filePath := "pid"
	// 将字符串转换为字节切片
	data := []byte(pid)
	// 创建或覆盖文件
	err = ioutil.WriteFile(filePath, data, 0644)
	if err != nil {
		logrus.Error("写入PID出错: %v\n", err)
		return
	} else {
		logrus.Debug("当前进程的PID:", pid)
	}
	for {
		logrus.Info("当前连接总数:", TotalNumberConnections)
		time.Sleep(5 * time.Minute)
	}
}

func forward(fromaddr, toaddr string) {
	//源端口，目的端口
	// 开始连接 fromaddr 监听地址, 创建监听器
	fromlistener, err := net.Listen("tcp", fromaddr)
	if err != nil {
		logrus.Error("无法监听:" + fromaddr + ",错误:" + err.Error())
		return
	}
	defer func(fromlistener net.Listener) {
		err := fromlistener.Close()
		if err != nil {
			logrus.Error("关闭监听:" + fromaddr + ",错误:" + err.Error())
		}
	}(fromlistener)
	// 循环处理连接的请求
	for {
		// 接收监听器,监听到的连接请求对象
		fromcon, err := fromlistener.Accept()
		if err != nil {
			logrus.Error("无法接受请求,错误:" + err.Error())
			//继续监听下一个连接
			continue
		}
		// id := uuid.New().String()[:8]
		remarks := fromaddr + " ----> " + toaddr
		logrus.Debug("新连接: " + fromaddr + " ----> " + toaddr)
		go handle(fromcon, toaddr, remarks)

	}
}
func handle(sconn net.Conn, rip, remarks string) {
	startTime := time.Now()
	defer func(sconn net.Conn) {
		err := sconn.Close()
		if err != nil {
			logrus.Error("sconn连接关闭错误!," + err.Error())
		}
	}(sconn)
	// 连接目标ip地址
	//dconn, err := net.Dial("tcp", rip)
	dconn, err := net.DialTimeout("tcp", rip, timeout)
	if err != nil {
		logrus.Error("连接错误:" + remarks + "错误:" + err.Error())
		return
	}
	mutex.Lock()
	TotalNumberConnections++
	mutex.Unlock()
	ExitChan1 := make(chan bool)
	ExitChan2 := make(chan bool)
	// 端口互换数据
	go func(sconn net.Conn, dconn net.Conn, Exit chan bool) {
		// 转发数据
		i, err := io.Copy(dconn, sconn)
		if err != nil {
			logrus.Error(remarks + "拷贝字节数:" + strconv.FormatInt(i, 10) + ",数据拷贝错误:" + "错误:" + err.Error())
			return
		}
		ExitChan1 <- true
		close(ExitChan1)
	}(sconn, dconn, ExitChan1)
	go func(sconn net.Conn, dconn net.Conn, Exit chan bool) {
		i, err := io.Copy(sconn, dconn)
		if err != nil {
			logrus.Error(remarks + "拷贝字节数:" + strconv.FormatInt(i, 10) + ",数据拷贝错误:" + "错误:" + err.Error())
			return
		}
		ExitChan2 <- true
		close(ExitChan2)
	}(sconn, dconn, ExitChan2)
	// 等待端口间数据交换完成
	<-ExitChan1
	<-ExitChan2
	err = dconn.Close()
	if err != nil {
		logrus.Error("dconn连接关闭错误!," + err.Error())
	}
	mutex.Lock()
	TotalNumberConnections--
	mutex.Unlock()
	defer func() {
		logrus.Debug(remarks + ",数据交换完毕,时间" + strconv.FormatFloat(time.Since(startTime).Seconds(), 'f', 2, 64) + " 秒")
	}()
}

func main() {
	//cmd.Execute()
	Run()
}
